//    LunchIT - Tool to make decisions of a lunch restaurant.
//    Copyright (C) 2010 Jukka Hell
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.

package fi.hell.lunchit

import fi.hell.util.CookieService

class Round {

	def grailsApplication
	
	static hasMany = [votes:Vote]
	static final int MAX_VOTE_COUNT = 3
    static final int MAX_VOTE_SUM = 6
	
	Calendar day
	Restaurant selection
	User user
	boolean confirmed = false
    boolean luncheonVoucherUsed = false
    Integer price
    Integer originalPrice

    static transients = ["grailsApplication"]
	
    static constraints = {
		votes           (size:0..MAX_VOTE_COUNT)
		day             (nullable:false)
		selection       (nullable:true)
		user            (nullable:false)
        price           (nullable:true)
        originalPrice   (nullable:true)
    }
	
	String toString() {
		def g = grailsApplication.mainContext.getBean('org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib')
		return g.formatDate(date:day.getTime(), type:"date", locale:"fi_FI", style:"MEDIUM")
	}
	
	/**
	 * Return top x restaurants and vote counts at today's round
	 */
	static Map<String, Integer> top(int max = 20) {
		return top(today(), max)
	}
	
	/**
	 * Return top x restaurants and vote counts of given day
	 */
	static Map<String, Integer> top(Calendar day, int max = 20) {
		day.set(Calendar.HOUR, 0)
        day.set(Calendar.HOUR_OF_DAY, 0)
		day.set(Calendar.MINUTE, 0)
		day.set(Calendar.SECOND, 0)
		day.set(Calendar.MILLISECOND, 0)
		
		List<Vote> votes = Vote.createCriteria().list {
			round {
				eq("day", day)
			}
//			projections {
//				groupProperty("restaurant")
//				sum("vote", "voteSum")
//			}
//			order("voteSum", "desc")
//			maxResults(max)
		}

		Map<String, Integer> topVotes = [:]
		for(Vote v in votes) {
            // Sum only votes that aren't yesterday's left overs
            if (v.round.yesterdaysLeftOvers() == "") {

                // If this votes restaurant hasn't got any votes yet, init is as 0
                topVotes[v.restaurant] = topVotes[v.restaurant] ?: 0
                topVotes[v.restaurant] += v.vote
            }
		}

        topVotes = topVotes.sort( { a, b -> b.value <=> a.value } )
		return topVotes
	}
	
	/**
	 * Return top selected restaurants from all time
	 */
	static Map<String,Integer> topSelections(int max = 20) {
		List results = Round.createCriteria().list {
			projections {
				groupProperty("selection")
				count("selection", "selectionCount")
			}
			order("selectionCount", "desc")
			maxResults(max)
		}

		Map<String, Integer> topSelections = [:]
		for(int i = 0; i < results.size(); i++) {
			if (results[i][1] > 0) {
				topSelections[results[i][0]] = results[i][1]
			}
		}

		return topSelections
	}
	
    /**
	 * Return top selected restaurants from todays round
	 */
	static Map<Restaurant, Integer> roundTopSelections(int max = 50) {
		Calendar now = today()
		List results = Round.createCriteria().list {
			eq("day", now)
			projections {
				groupProperty("selection")
				count("selection", "voteCount")
			}
			order("voteCount", "desc")
			maxResults(max)
		}

		Map<Restaurant, Integer> topSelections = [:]
		for(int i = 0; i < results.size(); i++) {
            if (results[i][1] > 0) {
                topSelections[results[i][0]] = results[i][1]
            }
		}
		
		return topSelections
	}
	
	/**
	 * Returns current round instance for logged in user
	 */
	static Round currentRound() {
		Calendar now = today()
        User loggedInUser = User.getLoggedInUser()
		Round roundInstance = Round.findByDayAndUser(now,loggedInUser)
		
		// Create new round if user has not yet voted
		if (!roundInstance && loggedInUser) {
			roundInstance = new Round()
			roundInstance.day = now
			roundInstance.user = loggedInUser
			roundInstance.save(flush:true)
			
		}
		return roundInstance
	}

    /**
	 * Returns current rounds for this day
	 */
	static List<Round> currentRounds() {
		Calendar now = today()
		return Round.findAllByDay(now)
	}
	
	/**
	 * Returns current vote number for logged in user. First vote is always max vote number.
	 */
	static int currentVote(Round r = null) {
        if (!r) {
            r = currentRound()
		}

        int currentVoteCount = 0
        for (Vote v in r?.votes) {
             currentVoteCount += v.vote
        }

        return Math.ceil((MAX_VOTE_SUM - currentVoteCount) / 2)
    }

	/**
	 * Return list of logged in user voted restaurant names 
	 */
    static List<Vote> ownVotes() {
        Calendar now = this.today()

        User user = User.getLoggedInUser()
        Round round = Round.createCriteria().get {
            eq("user", user)
            eq("day", now)
        }
        return round?.votes?.sort({it.vote})?.reverse() as List
    }
	
	/**
	 * Returns today's rounds
	 */
	static List<Round> userSelections(int max = 50) {
		Calendar now = this.today()
		List<Round> roundSelections = Round.createCriteria().list {
			eq("day", now)
			isNotNull("selection")
			maxResults(max)
		}
	}
	
	List<User> lunchMates(int max = 10) {
		return Round.createCriteria().list {
			eq("day", this.day)
			eq("selection", this.selection)
			ne("user", this.user)
		}?.user
	}

    /**
     * Check if selection was packed lunch but vote was some other restaurant
     * This means that user is eating yesterdays leftovers from the voted restaurant
     */
    String yesterdaysLeftOvers() {
        // Packed lunch is hard coded to be 18...this needs some refactoring
        if (this.selection?.id == Restaurant.PACKED_LUNCH_ID) {
            Vote vote = votes.find({ it.vote == 3 })
            if (vote?.restaurant?.name && vote.restaurant?.name != this.selection?.name) {
                return vote.restaurant?.name
            }
        }

        return ""
    }
    
    /**
     * Return this day, with hours, minutes, seconds and millis as zero
     */
    private static Calendar today() {
    	Calendar now = Calendar.getInstance()
		now.set(Calendar.HOUR, 0)
        now.set(Calendar.HOUR_OF_DAY, 0)
		now.set(Calendar.MINUTE, 0)
		now.set(Calendar.SECOND, 0)
		now.set(Calendar.MILLISECOND, 0)
		return now
    }
}
